package task;

import callBack.FileScannerCallBaack;
import lombok.Getter;
import java.io.File;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;


@Getter
public class FileScanner {
    //扫描文件个数
    private AtomicInteger fileNum = new AtomicInteger();
    //扫描文件夹的个数
    private AtomicInteger dirNum = new AtomicInteger(1);
    //所有扫描文件的子线程个数，只有当子线程个数为0时，主线程再继续执行
    private AtomicInteger threadCount = new AtomicInteger();
    //最后一个线程执行完，调用countDown方法唤醒线程
    private CountDownLatch latch = new CountDownLatch(1);
    //获取当前电脑可用的CPU个数
    private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
    //使用线程池创建对象
    private ThreadPoolExecutor pool = new ThreadPoolExecutor(CPU_COUNT,CPU_COUNT * 2,10, TimeUnit.SECONDS,
            new LinkedBlockingDeque<>(),new ThreadPoolExecutor.AbortPolicy());
    //文件扫描回调对象
    private FileScannerCallBaack callBack;

    public FileScanner(FileScannerCallBaack callBack) {
        this.callBack = callBack;
    }
    /**
     * 根据传入的文件夹进行扫描
     * @param filePath
     */
    public void scan(File filePath) {
        System.out.println("开始文件扫描任务，根目录为：" +filePath);
        long start = System.nanoTime();
        scanInternal(filePath);
        threadCount.incrementAndGet();
        try {
            latch.await();
        } catch (InterruptedException e) {
            System.err.println("扫描任务中断，根目录为：" +filePath);
        }finally {
            System.out.println("关闭线程池......");
            //当前所有子线程都执行完毕就正常关闭，若需中断任务需要立刻停止所有还在扫描的子线程
            pool.shutdown();
        }
        long end = System.nanoTime();
        System.out.println("文件扫描任务结束，共耗时：" +(end - start) * 1.0 / 1000000 + "ms");
        System.out.println("文件扫描任务结束，根目录为：" + filePath);
        System.out.println("共扫描到：" +fileNum.get() +"个文件");
        System.out.println("共扫描到：" +dirNum.get() +"个文件夹");
    }

    /**
     * 具体扫描任务的子线程递归方法
     * @param filePath
     */
    private void scanInternal(File filePath) {
        if(filePath == null) return;
        pool.submit(() -> {
            //使用回调函数，将当前目录下的所有内容保存到终端
            this.callBack.callback(filePath);
            //获取当前这一级目录下的file对象
            File[] files = filePath.listFiles();
            for(File file : files) {
                if(file.isDirectory()) {
                    dirNum.incrementAndGet();
                    threadCount.incrementAndGet();
                    scanInternal(file);
                }else {
                    fileNum.incrementAndGet();
                }
            }
            System.out.println(Thread.currentThread().getName() + "扫描:" +filePath +"任务结束");
            threadCount.decrementAndGet();
            if(threadCount.get() == 0) {
                System.out.println("所有扫描任务结束");
                //唤醒主线程
                latch.countDown();
            }
        });
    }
}
